﻿using System;
using Castle.Facilities.NHibernateIntegration;
using NHibernate;
using Castle.Services.Transaction;
using NHibernate.Criterion;
using System.Collections.Generic;
using Hippo.Core.Model;
using System.Linq;
using NHibernate.Linq;

namespace Hippo.Core.Data
{
    [Transactional]
    public class GenericNHibernateRepository : IRepository
    {
        private ISessionManager sessionManager;
        private string alias = null;

        public GenericNHibernateRepository(ISessionManager sessionManager, string alias)
        {
            this.sessionManager = sessionManager;
            this.alias = alias;
        }

        public GenericNHibernateRepository(ISessionManager sessionManager) 
            : this(sessionManager, null)
        {            
        }

        public T GetById<T>(object id)
        {
            using (ISession session = OpenSession())
            {
                return session.Get<T>(id);
            }
        }

        public T GetByKey<T>(string key, object value)
        {
            using (ISession session = OpenSession())
            {
                ICriteria criteria = session.CreateCriteria(typeof(T));
                criteria.Add(Restrictions.Eq(key, value));
                criteria.SetFirstResult(0);
                criteria.SetMaxResults(1);
                return criteria.UniqueResult<T>();
            }
        }

        [Transaction(TransactionMode.Requires)]
        public void Create<T>(T entity)
        {
            using (ISession session = OpenSession())
            {
                session.Save(entity);
                session.Flush();
            }
        }

        [Transaction(TransactionMode.Requires)]
        public void Update<T>(T entity)
        {
            using (ISession session = OpenSession())
            {
                session.Update(entity);
                session.Flush();
            }
        }

        [Transaction(TransactionMode.Requires)]
        public void Delete<T>(T entity)
        {
            using (ISession session = OpenSession())
            {
                session.Delete(entity);
                session.Flush();
            }
        }

        public ISession OpenSession()
        {
            if (alias == null)
            {
                return sessionManager.OpenSession();
            }
            else
            {
                return sessionManager.OpenSession(alias);
            }
        }

        public IList<T> FindBySpecification<T>(ISpecification<T> specification)
        {            
            using (ISession session = OpenSession())
            {
                return session.Linq<T>().Where<T>(specification.SatisfiedBy).ToList<T>();
            }
        }

        public T FindFirst<T>(ISpecification<T> specification)
        {
            using (ISession session = OpenSession())
            {
                return session.Linq<T>().Where<T>(specification.SatisfiedBy).First<T>();
            }
        }
    }
}
